"use client"; /** * StorefrontSignUpClient * * Custom email + password sign-up for the storefront. * Phone number is NEVER required. It is collected optionally on /activate/[orderId]. * * Clerk v6 API (storefront uses @clerk/nextjs ^6): * useSignUp() → { signUp, isLoaded } * useClerk() → { setActive } */ import { useState, FormEvent } from "react"; import { useSignUp } from "@clerk/nextjs"; import { useClerk } from "@clerk/nextjs"; import { useSearchParams, useRouter } from "next/navigation"; import { completeEmailSignUpAfterVerify } from "@/lib/complete-email-sign-up"; import { sanitizeAuthRedirect } from "@/lib/sanitize-auth-redirect"; type FlowStep = "credentials" | "verify" | "complete"; export function StorefrontSignUpClient() { const searchParams = useSearchParams(); const rawRedirect = searchParams.get("redirect_url") ?? undefined; const redirectTarget = sanitizeAuthRedirect(rawRedirect, "/dashboard"); const { signUp, isLoaded } = useSignUp(); const { setActive } = useClerk(); const router = useRouter(); const [step, setStep] = useState("credentials"); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [code, setCode] = useState(""); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); function clerkErrMsg(err: unknown): string { const e = err as { errors?: Array<{ longMessage?: string; message?: string }> }; return ( e?.errors?.[0]?.longMessage ?? e?.errors?.[0]?.message ?? "Something went wrong. Please try again." ); } async function handleCredentials(e: FormEvent) { e.preventDefault(); if (!isLoaded || !signUp) return; setError(null); setLoading(true); try { await signUp.create({ emailAddress: email, password }); await signUp.prepareEmailAddressVerification({ strategy: "email_code" }); setStep("verify"); } catch (err) { setError(clerkErrMsg(err)); } finally { setLoading(false); } } async function handleVerify(e: FormEvent) { e.preventDefault(); if (!isLoaded || !signUp) return; setError(null); setLoading(true); try { const result = await signUp.attemptEmailAddressVerification({ code }); const completion = await completeEmailSignUpAfterVerify(signUp, result); if (completion.status === "complete") { setStep("complete"); await setActive({ session: completion.sessionId }); router.replace(redirectTarget); } else { setError(completion.message); } } catch (err) { setError(clerkErrMsg(err)); } finally { setLoading(false); } } return (
{step === "credentials" && ( <>

Create account

Email and password — no phone required.

setEmail(e.target.value)} className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-sm text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none" placeholder="you@example.com" />
setPassword(e.target.value)} className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-sm text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none" placeholder="Min. 8 characters" />
{error && (

{error}

)}

Already have an account?{" "} Sign in

)} {step === "verify" && ( <>

Check your email

We sent a 6-digit code to {email}.

setCode(e.target.value.replace(/\D/g, ""))} className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-center text-lg tracking-[0.4em] text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none" placeholder="000000" />
{error && (

{error}

)}
)} {step === "complete" && (

Setting up your account…

)}
); }